#ifndef DATA_NODE_H
#define DATA_NODE_H

#include <limits>
#include <memory>
#include <string>

#include <sys/stat.h>
#include <unistd.h>

#include "types.h"
#include "bigBuffer.h"

class DataNode
{
private:
    DataNode(const DataNode &) = delete;
    DataNode &operator=(const DataNode &) = delete;
    static constexpr uint64_t FAKE_ID{std::numeric_limits<uint64_t>::max()};
    enum class NodeState
    {
        CLOSED,
        OPENED,
        VIRTUAL_SYMLINK,
        CHANGED,
        NEW
    };

    uint64_t _id;
    std::unique_ptr<BigBuffer> _buffer;
    int _open_count;
    NodeState _state;

    uint64_t _size;
    bool _has_btime, _metadataChanged;
    mode_t _mode;
    dev_t _device;
    struct timespec _mtime, _atime, _ctime, _btime;
    uid_t _uid;
    gid_t _gid;

    DataNode(uint64_t id, mode_t mode, uid_t uid, gid_t gid, dev_t dev);

public:
    static std::shared_ptr<DataNode> createNew(mode_t mode, uid_t uid, gid_t gid, dev_t dev);
    static std::shared_ptr<DataNode> createTmpDir(mode_t mode, uid_t uid, gid_t gid, dev_t dev);
    static std::shared_ptr<DataNode> createExisting(uint64_t id, struct stat fileStat);
    void chmod(mode_t mode);
    inline mode_t mode() const
    {
        return _mode;
    }
    inline dev_t device() const
    {
        return _device;
    }
    void setTimes(const timespec &atime, const timespec &mtime);
    void setCTime (const timespec &ctime);
    void touchCTime();
    inline struct timespec atime() const
    {
        return _atime;
    }
    inline struct timespec ctime() const
    {
        return _ctime;
    }
    inline struct timespec mtime() const
    {
        return _mtime;
    }
    void setUid(uid_t uid);
    void setGid(gid_t gid);
    inline uid_t uid() const
    {
        return _uid;
    }
    inline gid_t gid() const
    {
        return _gid;
    }
    uint64_t size() const;
    int open();
    int read(char *buf, size_t size, size_t offset);
    int file_read(char *buf, size_t size, size_t offset, const char *real_name, uint64_t real_file_size);
    int vsphere_read(char *buf, size_t size, size_t offset, uint64_t real_file_size, std::vector<int> &fd_vector, const int store_file, const std::unordered_map<int, std::vector<DataSegment>>& cache);
    int fsphere_read(char *buf, size_t size, size_t offset, const char *real_name, uint64_t real_file_size, const char *store_name);
    int write(const char *buf, size_t size, size_t offset);
    int file_write(const char *buf, size_t size, size_t offset, const char *real_name, uint64_t real_file_size);
    int vsphere_write(const char *buf, size_t size, size_t offset, uint64_t real_file_size, std::vector<int> &fd_vector, const int store_file, const std::unordered_map<int, std::vector<DataSegment>>& cache);
    int fsphere_write(const char *buf, size_t size, size_t offset, const char *real_name, uint64_t real_file_size, const char *store_name);
    int close();
    int truncate(size_t offset);
};
#endif
